/**
 * Copyright(c) 2014 Xunton Solutions
 *
 * History:
 *   15-4-2 下午10:59 Created by dwzhang
 */
package com.jade.bss.service.push.impl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import cn.jpush.api.JPushClient;

import cn.jpush.api.common.resp.APIRequestException;
import cn.jpush.api.common.resp.APIConnectionException;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.push.model.notification.WinphoneNotification;
import cn.jpush.api.report.ReceivedsResult;
import com.jade.bss.service.push.*;
import com.jade.framework.base.util.Parameters;
import org.apache.commons.lang3.StringUtils;

/**
 * 通过极光通道进行发送。
 *
 * @author <a href="mailto:dwzhang@xunton.com">dwzhang</a>
 * @version 1.0 15-4-2 下午10:59
 */
public class JPushService extends AbstractPushService
{
    public PushResult pushNotification(String appCode, String platform, String audienceType, String[] audiences,
            boolean isAnd, String title, String notification, Parameters serviceParams, Parameters pushParams)
            throws PushException
    {
        return push("notification", appCode, platform, audienceType, audiences, isAnd, title, notification,
                    serviceParams, pushParams);
    }

    public PushResult pushMessage(String appCode, String platform, String audienceType, String[] audiences,
            boolean isAnd, String title, String notification, Parameters serviceParams, Parameters pushParams)
            throws PushException
    {
        return push("message", appCode, platform, audienceType, audiences, isAnd, title, notification, serviceParams,
                    pushParams);
    }

    protected PushResult push(String type, String appCode, String platform, String audienceType, String[] audiences,
            boolean isAnd, String title, String notification, Parameters serviceParams, Parameters pushParams)
            throws PushException
    {
        //todo 暂时不考虑透传消息的情况。
        PushApp app = appConfig.getApp(appCode);
        JPushClient jpushClient = new JPushClient(app.getSecret(), app.getKey(), 3);
        PushPayload payload = buildPushObject(type, platform, audienceType, audiences, isAnd, title, notification,
                                              serviceParams, pushParams);
        try {
            cn.jpush.api.push.PushResult result = jpushClient.sendPush(payload);
            log.info("Got result - " + result);
            return new PushResult(result.isResultOK(), result.msg_id, result.sendno);
        }
        catch (Exception e) {
            throw handleException(e);
        }

    }

    public ReceivedResult getReport(String appCode, long msgId)
            throws PushException
    {
        PushApp app = appConfig.getApp(appCode);
        JPushClient jpushClient = new JPushClient(app.getSecret(), app.getKey());
        try {
            ReceivedsResult result = jpushClient.getReportReceiveds(String.valueOf(msgId));
            log.info("Got result - " + result);
            List<ReceivedsResult.Received> list = result.received_list;
            ReceivedResult rResult = new ReceivedResult();
            rResult.setMsgId(list.get(0).msg_id);
            rResult.setAndroidReceived(list.get(0).android_received);
            rResult.setIosSent(list.get(0).ios_apns_sent);
            return rResult;
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    protected PushPayload buildPushObject(String type, String platform, String audienceType, String[] audiences,
            boolean isAnd, String title, String notification, Parameters serviceParams, Parameters pushParams)
    {
        PushPayload.Builder builder = PushPayload.newBuilder();
        Map<String, String> extras = toMap(serviceParams);
        if (type.equals("notification")) {
            IosNotification.Builder iosNfBuilder = null;

            AndroidNotification.Builder androidNfBuilder = null;
            WinphoneNotification.Builder wpNfBuilder = null;


            if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_ANDROID_IOS)) {
                builder.setPlatform(Platform.android_ios());
                iosNfBuilder = IosNotification.newBuilder();
                androidNfBuilder = AndroidNotification.newBuilder();
            }
            else {
                if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_ANDROID)) {
                    builder.setPlatform(Platform.android());
                    androidNfBuilder = AndroidNotification.newBuilder();
                }
                else {
                    if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_IOS)) {
                        builder.setPlatform(Platform.ios());
                        iosNfBuilder = IosNotification.newBuilder();
                    }
                    else {
                        if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_WP)) {
                            builder.setPlatform(Platform.winphone());
                            wpNfBuilder = WinphoneNotification.newBuilder();
                        }
                        else {
                            builder.setPlatform(Platform.all());
                            iosNfBuilder = IosNotification.newBuilder();
                            androidNfBuilder = AndroidNotification.newBuilder();
                            wpNfBuilder = WinphoneNotification.newBuilder();
                        }
                    }
                }
            }
            if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_ALL)) {
                builder.setAudience(Audience.all());
            }
            else {
                if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_TAG)) {
                    if (isAnd) {
                        builder.setAudience(Audience.tag_and(audiences));
                    }
                    else {
                        builder.setAudience(Audience.tag(audiences));
                    }
                }
                else {
                    if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_ACCOUNT)) {
                        builder.setAudience(Audience.alias(audiences));
                    }
                    else {
                        if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_ID)) {
                            builder.setAudience(Audience.registrationId(audiences));
                        }
                    }
                }
            }

            if (serviceParams == null || serviceParams.isEmpty()) {
                builder.setNotification(Notification.alert(notification));
            }
            else {
                Notification.Builder notificationBuilder = Notification.newBuilder();
                if (androidNfBuilder != null) {
                    androidNfBuilder.setAlert(notification).addExtras(extras);
                    notificationBuilder = notificationBuilder.addPlatformNotification(androidNfBuilder.build());
                    androidNfBuilder.setTitle(title);
                }
                if (iosNfBuilder != null) {
                    iosNfBuilder.setAlert(notification).addExtras(extras);
                    notificationBuilder = notificationBuilder.addPlatformNotification(iosNfBuilder.build());
                }
                if (wpNfBuilder != null) {
                    wpNfBuilder.setAlert(notification).addExtras(extras);
                    notificationBuilder = notificationBuilder.addPlatformNotification(wpNfBuilder.build());
                }
                builder.setNotification(notificationBuilder.build());
            }

        }
        else {
            Message.Builder messageBuilder = Message.newBuilder();


            if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_ANDROID_IOS)) {
                builder.setPlatform(Platform.android_ios());
            }
            else {
                if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_ANDROID)) {
                    builder.setPlatform(Platform.android());
                }
                else {
                    if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_IOS)) {
                        builder.setPlatform(Platform.ios());
                    }
                    else {
                        if (StringUtils.equalsIgnoreCase(platform, PushConstants.PLATFORM_WP)) {
                            builder.setPlatform(Platform.winphone());
                        }
                        else {
                            builder.setPlatform(Platform.all());
                        }
                    }
                }
            }
            if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_ALL)) {
                builder.setAudience(Audience.all());
            }
            else {
                if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_TAG)) {
                    if (isAnd) {
                        builder.setAudience(Audience.tag_and(audiences));
                    }
                    else {
                        builder.setAudience(Audience.tag(audiences));
                    }
                }
                else {
                    if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_ACCOUNT)) {
                        builder.setAudience(Audience.alias(audiences));
                    }
                    else {
                        if (StringUtils.equalsIgnoreCase(audienceType, PushConstants.AUDIENCE_ID)) {
                            builder.setAudience(Audience.registrationId(audiences));
                        }
                    }
                }
            }
            messageBuilder.setTitle(title);
            messageBuilder.setMsgContent(notification);
            if (serviceParams != null && !serviceParams.isEmpty()) {
                messageBuilder.addExtras(extras);

            }
            builder.setMessage(messageBuilder.build());

        }

        Options options = buildOptions(pushParams);
        if (options != null) {
            builder.setOptions(options);
        }
        return builder.build();
    }

    protected Map<String, String> toMap(Parameters parameters)
    {
        if (parameters == null) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();
        Iterator<String> itor = parameters.getNames();
        while (itor.hasNext()) {
            String name = itor.next();
            map.put(name, parameters.getString(name));
        }
        return map;
    }

    protected Options buildOptions(Parameters pushParams)
    {
        if (pushParams != null && !pushParams.isEmpty()) {
            boolean apnsProduction = pushParams.getBoolean("apns_production", true);
            int timeToLive = pushParams.getInt("time_to_live", 86400);
            int sendno = pushParams.getInt("sendno", -1);
            int bigPushDuration = pushParams.getInt("big_push_duration", -1);
            long msgId = pushParams.getLong("override_msg_id", -1);
            Options.Builder optionsBuilder = Options.newBuilder();
            optionsBuilder.setApnsProduction(apnsProduction);
            optionsBuilder.setTimeToLive(timeToLive);
            if (sendno > 0) {
                optionsBuilder.setSendno(sendno);
            }
            if (msgId > 0) {
                optionsBuilder.setOverrideMsgId(msgId);
            }
            return optionsBuilder.build();
        }
        else {
            return null;
        }
    }

    protected PushException handleException(Exception e)
    {
        if (e instanceof APIConnectionException) {
            // Connection error, should retry later
            log.error("Connection error, should retry later", e);
            return new PushException(PushConstants.ERR_CONNECT, e.getMessage(), e);
        }
        else {
            if (e instanceof APIRequestException) {
                // Should review the error, and fix the request
                log.error("Should review the error, and fix the request", e);
                log.warn("HTTP Status: " + ((APIRequestException)e).getStatus());
                log.warn("Error Code: " + ((APIRequestException)e).getErrorCode());
                log.warn("Error Message: " + ((APIRequestException)e).getErrorMessage());
                if (((APIRequestException)e).getErrorCode() == 1011) {
                    return new PushException(PushConstants.ERR_NO_AUDIENCE,
                                             ((APIRequestException)e).getErrorCode() + ":" +
                                             ((APIRequestException)e).getErrorMessage(), e);
                }
                else {
                    return new PushException(PushConstants.ERR_REQUEST, ((APIRequestException)e).getErrorCode() + ":" +
                                                                        ((APIRequestException)e).getErrorMessage(), e);
                }
            }
            else {
                return new PushException(PushConstants.ERR_UNEXPECTED, e.getMessage(), e);
            }
        }
    }
}
